package org.shiro.auth.client.filter;

import java.io.IOException;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

@Component
@WebFilter("/*")
@Configuration
public class AuthenticationFilter implements Filter {

  @Value("${spring.profiles.active}")
  private String       active;
  @Value("${service}")
  private String       service;
  private final String ACCESS_TOKEN_COOKIE_NAME = "accessToken";

  AccessTokenRpc       accessTokenRpc           = new AccessTokenRpc();

  @PostConstruct
  public void init() {
    ConfigUtil.setActive(active);
  }

  public void init(FilterConfig filterConfig) throws ServletException {
  }

  /**
   * 鉴权三步曲： <br>
   * 1、获取cookie中的token<br>
   * 2、获取接口入参数中的token<br>
   * 3、鉴定有效性<br>
   */
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
      throws IOException, ServletException {
    HttpServletResponse rep = (HttpServletResponse) response;
    HttpServletRequest req = (HttpServletRequest) request;

    String accessToken = "";
    String cookieAccessToken = "";
    String paramAccessToken = req.getParameter(ACCESS_TOKEN_COOKIE_NAME);
    Cookie cookie = getCookie(req);

    if (cookie != null) {
      cookieAccessToken = cookie.getValue();
    }

    if (isNoneAccessToken(cookieAccessToken, paramAccessToken)) { // 没有登录
      rep.sendRedirect(getLoginUrlWithService(req));
      return;
    }

    if (StringUtils.isNotBlank(paramAccessToken)) {
      if (cookie == null) { // 添加
        cookie = new Cookie(ACCESS_TOKEN_COOKIE_NAME, paramAccessToken);
        cookie.setPath("/");
        rep.addCookie(cookie);
      } else {// 更新
        if (StringUtils.isNotBlank(paramAccessToken) && !paramAccessToken.equals(cookieAccessToken)) {
          cookie = new Cookie(ACCESS_TOKEN_COOKIE_NAME, paramAccessToken);
          cookie.setPath("/");
          rep.addCookie(cookie);
        }
      }
    }

    accessToken = StringUtils.isNotBlank(paramAccessToken) ? paramAccessToken : cookieAccessToken;// 指定为主，就近原则
    boolean isValid = accessTokenRpc.isValid(accessToken);
    if (isValid) {
      chain.doFilter(request, response);
    } else {
      rep.sendRedirect(getLoginUrlWithService(req));
    }
  }

  private boolean isNoneAccessToken(String cookieAccessToken, String paramAccessToken) {
    return StringUtils.isBlank(cookieAccessToken) && StringUtils.isBlank(paramAccessToken);
  }

  private Cookie getCookie(HttpServletRequest request) {
    javax.servlet.http.Cookie cookies[] = request.getCookies();
    if (cookies != null) {
      for (javax.servlet.http.Cookie cookie : cookies) {
        if (cookie.getName().equals(ACCESS_TOKEN_COOKIE_NAME)) {
          return cookie;
        }
      }
    }
    return null;
  }

  public void destroy() {
  }

  private String getLoginUrlWithService(HttpServletRequest request) {
    String baseUrl = ConfigUtil.getHost() + "/login?service=" + service;
    if (request != null) {
      String uri = request.getRequestURI();
      if (StringUtils.isNotBlank(uri)) {
        baseUrl += uri;
        Map<String, String[]> params = request.getParameterMap();
        if (MapUtils.isNotEmpty(params)) {
          String paramsUrl = buildParam(params);
          if (StringUtils.isNotBlank(paramsUrl)) {
            baseUrl += "?";
            paramsUrl = paramsUrl.substring(0, paramsUrl.length() - 1);
            baseUrl += paramsUrl;
          }
        }
      }
    }
    return baseUrl;
  }

  private String buildParam(Map<String, String[]> params) {
    String queryString = "";
    for (String key : params.keySet()) {
      if (key.equals(ACCESS_TOKEN_COOKIE_NAME)) {
        continue;
      }
      String[] values = params.get(key);
      for (int i = 0; i < values.length; i++) {
        String value = values[i];
        queryString += key + "=" + value + "&";
      }
    }
    return queryString;
  }
}
